#include <android/log.h>
#include <string>
#include <jni.h>

#undef	LOG_TAG
#define LOG_TAG "AACDecoder"
#define LOGI(...) __android_log_print (ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print (ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

extern "C" {
    #include "libavutil/opt.h"
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libswscale/swscale.h"
    #include "libswresample/swresample.h"
};

#define FFMPEG_TAG "ffmpeg"
int gdata_size = 0;

void logcat(void* ptr, int level, const char* fmt,va_list vl){
    __android_log_print (ANDROID_LOG_VERBOSE+level,FFMPEG_TAG,fmt, vl);
}

extern "C" jstring
Java_com_hhw_musicplayer_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {

    av_log_set_callback(logcat);

    std::string hello = "Hello from C++";
    return env->NewStringUTF(avcodec_configuration());
}

extern "C" void
Java_com_hhw_musicplayer_MainActivity_playMusic(
        JNIEnv* env,
        jobject /* this */,
        jstring path,
        jstring dstPath
        ) {

    av_log_set_callback(logcat);

    const char* url = env->GetStringUTFChars(path,NULL);
    LOGI("open file %s",url);


    avformat_network_init();

    AVFormatContext* pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx,url,NULL,NULL) < 0){
        LOGI("can not open url: %s",url);
        return;
    }

    if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        LOGI("can not find streams from");
        return;
    }

    AVCodecParameters* codecpar;
    int audioStreamIndex;//音频流的索引

    for(int i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if(pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)//得到音频流
        {
            audioStreamIndex = i;
            codecpar = pFormatCtx->streams[i]->codecpar;

            LOGI("采样率:%d,"
                 "\n音频总时长:%lld"
                 "\n,时间基：%f"
                 "\n编码类型:%d"
                 ,codecpar->sample_rate
                 ,pFormatCtx->duration / AV_TIME_BASE
                 ,pFormatCtx->streams[i]->time_base.den/(pFormatCtx->streams[i]->time_base.num*1.0)
                 ,codecpar->codec_id
                 );
            break;
        }
    }

    AVCodec *dec = avcodec_find_decoder(codecpar->codec_id);
    if(!dec)
    {
        LOGE("can not find decoder");
        return;
    }

    AVCodecContext* avCodecContext = avcodec_alloc_context3(dec);
    if(!avCodecContext)
    {
        LOGE("can not alloc new decodecctx");
        return;
    }

    if(avcodec_parameters_to_context(avCodecContext, codecpar) < 0)
    {
        LOGE("can not fill decodecctx");
        return;
    }

    if(avcodec_open2(avCodecContext, dec, 0) != 0)
    {
        LOGE("cant not open audio strames");
        return;
    }

    uint8_t* buffer = nullptr;
    int buffer_size = 0;

    FILE* wfile = fopen(env->GetStringUTFChars(dstPath,NULL),"w");//将解码的PCM数据保存到该文件
    if (wfile == nullptr){
        LOGE("open wfile fail");
        return;
    }

    //准备就绪，开始解码音频文件内容
    while (true){
        AVPacket *avPacket = av_packet_alloc();

        if (av_read_frame(pFormatCtx, avPacket) != 0){
            //播放结束
            av_packet_free(&avPacket);
            av_free(avPacket);
            break;
        }

        if (avPacket->stream_index != audioStreamIndex){
            //不是音频帧
            av_packet_free(&avPacket);
            av_free(avPacket);
            continue;
        }

        int ret = avcodec_send_packet(avCodecContext,avPacket);

        if (ret){
            AVFrame* avFrame = av_frame_alloc();
            while (avcodec_receive_frame(avCodecContext, avFrame) == 0){
                if(avFrame->channels && avFrame->channel_layout == 0)
                {
                    avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);
                }
                else if(avFrame->channels == 0 && avFrame->channel_layout > 0)
                {
                    avFrame->channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);
                }

                SwrContext *swr_ctx;

                swr_ctx = swr_alloc_set_opts(
                        NULL,
                        avFrame->channel_layout,
                        AV_SAMPLE_FMT_S16,
                        avFrame->sample_rate,
                        avFrame->channel_layout,
                        (AVSampleFormat) avFrame->format,
                        avFrame->sample_rate,
                        NULL, NULL
                );

                if(!swr_ctx || swr_init(swr_ctx) <0)
                {
                    swr_free(&swr_ctx);
                    break;
                }

                //获取输出的一帧的PCM数据的大小
                int temp_size = av_samples_get_buffer_size(nullptr,avCodecContext->channels, avFrame->nb_samples, AV_SAMPLE_FMT_S16, 1);

                //帧的大小是相对固定的，但不排除大小改变的可能
                if (buffer_size != temp_size){
                    buffer_size = temp_size;

                    if (buffer != nullptr){
                        av_free(buffer);
                    }

                    buffer = (uint8_t *) av_malloc(buffer_size);
                }


                if (avFrame->nb_samples != buffer_size){
                    LOGI("nb_samples %d",avFrame->nb_samples);
                }

               int nb =
                       swr_convert(
                        swr_ctx,
                        &buffer,
                        buffer_size,//网上部分案例这个值采用avFrame->nb_samples，但个人测试不可行，会导致音频播放速度变快及有杂音,avFrame->nb_sample*通道数（一般为1或2）*单个采样占用的字节数(S16就是2) = buffer_size
                        (const uint8_t **) avFrame->data,
                        avFrame->nb_samples);

//                int out_channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);
//                int data_size = nb * out_channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
                //网上部分案例会采用data_size来作为最终写入文件的大小，但个人测试这个值和buffer_size是一样的

                //将解码后的PCM保存进文件,在实际场景中，回调给Java层或直接播放也是可以的,毕竟已知采样率，通道数等信息
                if (nb > 0){
                    fwrite(buffer,1,buffer_size,wfile);
                }

                swr_free(&swr_ctx);
            }

            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
        }
    }

    LOGI("播放结束");

}
